/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Connection module implementation for TCP links.
 * \author Antti Maula <antti.maula@tkk.fi>
 */


#define _GNU_SOURCE

#include "owndebug.h"
#include "connectionTCP.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <linux/sockios.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <netdb.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>

//**************************************************************************************
//**************************************************************************************
//**************************************************************************************
static int clientConnect(TConnectionTCP *con);
static int serverConnect(TConnectionTCP *con);
//**************************************************************************************
//**************************************************************************************
//**************************************************************************************
//**************************************************************************************

const TConnectionPtr *connectionTCPGetConnectionPtrEntry(const TConnectionTCP *con)
{
  return &con->connectionPtr;
}
//**************************************************************************************

int connectionTCPInit(TConnectionTCP *con)
{
  // Zero struct. Will set all variables to Zero initially.
  memset(con, 0x00, sizeof(TConnectionTCP));


  // Set status and style defaults.
  con->connectionStatus = tcpStatusDisconnected;
  con->connectionStyle = tcpStyleClient;

  // Init descriptors to invalid values.
  con->socket_fd = -1;
  con->data_fd = -1;

  // Assign pointer parameters
  con->connectionPtr.dataPtr = con;
  con->connectionPtr.sendFunc = (sendDataFuncPtr)connectionTCPSendData;
  con->connectionPtr.receiveFunc = (receiveDataFuncPtr)connectionTCPReceiveData;
  con->connectionPtr.receiveTimedFunc = (receiveTimedDataFuncPtr)connectionTCPReceiveDataTimed;
  con->connectionPtr.connectFunc = (connectFuncPtr)connectionTCPConnect;
  con->connectionPtr.disconnectFunc = (disconnectFuncPtr)connectionTCPDisconnect;
  con->connectionPtr.getHandleFunc = (getHandleFuncPtr)connectionTCPGetHandle;
  con->connectionPtr.deinitFunc = (deinitFuncPtr)connectionTCPDeinit;
  con->connectionPtr.getPeerIPFunc = (getPeerIPFuncPtr)connectionTCPGetPeerIP;
  con->connectionPtr.getPeerPortFunc = (getPeerPortFuncPtr)connectionTCPGetPeerPort;

  // Initialize mutex. Don't want concurrent access over 'Connection'
  //  r = pthread_mutex_init(&con->mutex, NULL);
  //assert(r == 0);
      
  return CONNECTIONTCP_OK;
}
//**************************************************************************************

const char *connectionTCPGetPeerIP(const TConnectionTCP *con)
{
  assert(con != NULL);
  return inet_ntoa(con->socket_addr.sin_addr);
}
//**************************************************************************************

int connectionTCPGetPeerPort(const TConnectionTCP *con)
{
  assert(con != NULL);
  return (int)(con->socket_addr.sin_port);
}
//**************************************************************************************

int connectionTCPDeinit(TConnectionTCP *con)
{
  // Attempt to close it. (May return error, doesn't matter here)
  connectionTCPDisconnect(con);

  // Free resources
  if (con->host != NULL) 
    free(con->host);
  /*  if (con->socket_fd != -1)
      close(con->socket_fd);*/
  
  // Destroy the mutex
  //  pthread_mutex_destroy(&con->mutex);
  
  return 0;
}
//**************************************************************************************

int connectionTCPGetHandle(TConnectionTCP *con)
{
  return con->data_fd;
}
//**************************************************************************************

int connectionTCPCreateListeningSocket(int port, struct sockaddr_in *socket_addr)
{
#ifdef ENABLE_REUSEPORT
  int optionValue = 0;
#endif
  int socket_fd;
  int r;
  int flag = 1;

  if (port <= 0 || socket_addr == NULL) 
    return CONNECTIONTCP_ERROR;
 
  // Create Socket for listening
  if ((socket_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    dPrint(1, "Failed to CREATE a socket!"); 
    return CONNECTIONTCP_ERROR;
  }

  // Marking the PORT REUSABLE, this will allow connections reopened before
  // the 60 seconds socket-timeout has passed.
  // NOTE; All systems don't support this. (therefore the #ifdef)
#ifdef ENABLE_REUSEPORT
  setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &optionValue, sizeof(int));
#endif

  // Set TCP_NODELAY socket option.
  r = setsockopt(socket_fd,
                 IPPROTO_TCP,
                 TCP_NODELAY,
                 (char *) &flag,
                 sizeof(int));
  if (r < 0) {
    dPrint(2,"Failed to set TCP_NODELAY Socket option!");
  } else {
    dPrint(3,"Succesfully enabled TCP_NODELAY for socket %d", socket_fd);
  }
  
  // Zero the address structure (required for maximum portability)
  memset(socket_addr, 0x00, sizeof(struct sockaddr_in));;
  
  // sets few struct values
  socket_addr->sin_family = AF_INET; // Internet protocol
  socket_addr->sin_port = htons(port); // Store port
  socket_addr->sin_addr.s_addr = INADDR_ANY; // Any address
  
  // Binds a name to a socket
  if (bind(socket_fd,
           (struct sockaddr *)socket_addr,
           sizeof(struct sockaddr_in)) < 0) {
    dPrint(1, "Failed to BIND a socket!");
    return CONNECTIONTCP_ERROR;
  }
  
  // Listens for socket connections and
  // limits the backlog of incoming connections to 20
  if (listen(socket_fd, 20) != 0) {
    dPrint(1, "Failed to LISTEN for socket!");
    return CONNECTIONTCP_ERROR;
  } 
  
  return socket_fd;
}
//**************************************************************************************

int connectionTCPSetConnectionType(TConnectionTCP *con, 
                                   EConnectionTCPStyle style, 
                                   EConnectionTCPBlockingType blockingType)
{
  // Store blocking flag value.
  con->blockingType = blockingType;
  con->connectionStyle = style;

  return CONNECTIONTCP_OK;
}
//**************************************************************************************

int connectionTCPSetSocketHandle(TConnectionTCP *con, int socket_fd, 
                                 const struct sockaddr_in *socket_addr)
{
  if (con->connectionStyle != tcpStyleServer || socket_addr == NULL || socket_fd < 0) {
    return CONNECTIONTCP_ERROR;
  }
  con->socket_fd = socket_fd;
  memcpy(&con->socket_addr, socket_addr, sizeof(struct sockaddr_in));

  return CONNECTIONTCP_OK;
}
//**************************************************************************************

static int listeningSocketConnect(TConnectionTCP *con)
{
  int result = CONNECTIONTCP_ERROR;

  // Lock
  //  pthread_mutex_lock(&con->mutex);

  // If con->socket_fd is previously unopened, create it now.
  if (con->socket_fd == -1) {
    con->socket_fd = connectionTCPCreateListeningSocket(con->port, &con->socket_addr);
  }

  // Check result (just a formality here)
  if (con->socket_fd > 0) {
    // Now, assign the socket_fd directly to data_fd.
    con->data_fd = con->socket_fd;
    result = CONNECTIONTCP_OK;
  }

  // Unlock
  //  pthread_mutex_unlock(&con->mutex);


  return result;
}
//**************************************************************************************

static int serverConnect(TConnectionTCP *con)
{
  int result = CONNECTIONTCP_ERROR;
  int r;
  int flag = 1;
  socklen_t address_length = sizeof(con->socket_addr);
#ifdef ENABLE_REUSEPORT
  int optionValue = 1;
#endif

  // Lock
  //  pthread_mutex_lock(&con->mutex);

  // If con->socket_fd is previously unopened, create it now.
  if (con->socket_fd == -1) {
    con->socket_fd = connectionTCPCreateListeningSocket(con->port, &con->socket_addr);
  }

  // Test whether the socket is now open
  if (con->socket_fd != -1) {
    
    // Needed with accept
    do { // Accepts a new connection on a socket, waits until connection OK.
      con->data_fd = accept(con->socket_fd,
                            (struct sockaddr*)&con->socket_addr,
                            &address_length);
    } while (con->data_fd == -1 && errno == EINTR);
    
    // Only proceed if connection accepted OK.
    if (con->data_fd != -1) {
      // Log the successfull connection.
      dPrint(2, "Connection from IP: %s", inet_ntoa(con->socket_addr.sin_addr));
      con->connectionStatus = tcpStatusConnected; // Assign current "connectedstate" flag
      
      // Set the stream NON BLOCKING! (on demand)
      if (con->blockingType == tcpBlockingTypeNonBlocking) {
        r = fcntl(con->data_fd, F_SETFL, O_NONBLOCK);
        assert(r == 0);
      }

      // Set the socket TCP_NODELAY option
      r = setsockopt(con->data_fd,
                     IPPROTO_TCP,
                     TCP_NODELAY,
                     (char *) &flag,
                     sizeof(int));
      if (r < 0) {                                                                                                              
        dPrint(2,"Failed to set TCP_NODELAY Socket option for accept():ed socket %d!",con->data_fd);
      } else {                                                                                                                  
        dPrint(3,"Succesfully enabled TCP_NODELAY for socket %d", con->data_fd);                                                   
      }                                     
                                                                                          
      // Finally, mark the return value to be OK.
      result = CONNECTIONTCP_OK;
    
    } else { 
      dPrint(1, "Failed to ACCEPT a connection");

    } // else if not (con->data_fd != -1) 
  } // if (con->socket_fd != -1)

  // Unlock
  //  pthread_mutex_unlock(&con->mutex);


  return result;
}
//**************************************************************************************

static int asyncClientConnect(TConnectionTCP *con)
{
  int tr, result = CONNECTIONTCP_ERROR;
  assert(con->inet_addr.s_addr != 0);
  assert(con->port != -1);
  
  // Lock
  //  pthread_mutex_lock(&con->mutex);

  // Cleanup before entering.
  if (con->socket_fd > 0) {
    close(con->socket_fd);
    con->data_fd = con->socket_fd = -1;
  }
  
  // Create socket.
  if ((con->socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    dPrint(1, "Unable to create socket!");
    
  } else {
    // Attempt to set the socket in NON-BLOCKING mode.
    tr = fcntl(con->socket_fd, F_SETFL, O_NONBLOCK);
    assert(tr == 0); 
  
    //Zero socket addr and set required parameters
    memset(&con->socket_addr, 0x00, sizeof(con->socket_addr));
    con->socket_addr.sin_family = AF_INET; // Set to INET type
    con->socket_addr.sin_port = htons(con->port); // Converts the port value to netform
    con->socket_addr.sin_addr = con->inet_addr;
    
    // Now we have all properties required.
    if ( connect(con->socket_fd, (struct sockaddr *)&con->socket_addr, 
                 sizeof(con->socket_addr)) != 0 && errno != EINPROGRESS) {
      dPrint(ODERROR, "Error connecting to target '%s' port %d!", 
             inet_ntoa(con->inet_addr),
             con->port);
      close(con->socket_fd); // Close the socket.
      
    } else {
      // Succesfully connected as Client.
      memset(&con->socket_status, 0x00, sizeof(con->socket_status)); // Zero socket status

      // Fill in POLL structure for watchin the socket status.
      con->socket_status.fd = con->socket_fd; // Set status field to current socket
      con->socket_status.events = POLLHUP | POLLERR | POLLNVAL; // set flags
      con->connectionStatus = tcpStatusConnected; // Assign current "connectedstate" flag
      
      con->data_fd = con->socket_fd; // Copy to data handle

      int flag = 1;
      const int r = setsockopt(con->data_fd,
                               IPPROTO_TCP,
                               TCP_NODELAY,
                               (char *) &flag,
                               sizeof(int));
      if (r < 0) {                                                                                                              
        dPrint(2,"Failed to set TCP_NODELAY Socket option for accept():ed socket %d!",con->data_fd);
      } else {                                                                                                                  
        dPrint(3,"Succesfully enabled TCP_NODELAY for socket %d", con->data_fd);                                                   
      }                        
      
	
      // Again the same - required?
      // Modify NON-BLOCKING state according to requested value.
      if (con->blockingType == tcpBlockingTypeNonBlocking) {
        int fr = fcntl(con->data_fd, F_SETFL, O_NONBLOCK);
        assert(fr == 0);
      }
      
      dPrint(ODVINFO, "Connecting to target '%s' at port %d successfully initiated.", 
             inet_ntoa(con->inet_addr),
             con->port);
      
      // Finally, set result to OK
      result = CONNECTIONTCP_OK;
      
    } // if (connect...)
  } // if(con->hostEntry...)
  
  // UNLOCK
  //  pthread_mutex_unlock(&con->mutex);
  
  return result;
}
//**************************************************************************************

static int clientConnect(TConnectionTCP *con)
{
  int result = CONNECTIONTCP_ERROR;

  if (con->inet_addr.s_addr != 0 && con->port != -1) {
    
    // Lock
    //  pthread_mutex_lock(&con->mutex);
    
    // Create socket.
    if ((con->socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      dPrintLCRed(ODERROR, "Unable to create socket!");
      
    } else {
      
      //Zero socket addr and set required parameters
      memset(&con->socket_addr, 0x00, sizeof(con->socket_addr));
      con->socket_addr.sin_family = AF_INET; // Set to INET type
      con->socket_addr.sin_port = htons(con->port); // Converts the port value to netform
      con->socket_addr.sin_addr = con->inet_addr;
      
      // Now we have all properties required.
      if ( connect(con->socket_fd, (struct sockaddr *)&con->socket_addr, 
                   sizeof(con->socket_addr)) != 0 ) {
        dPrint(ODERROR, "Error connecting to target '%s' port %d!", 
               inet_ntoa(con->inet_addr),
               con->port);
        //close(con->socket_fd); // Do very clean! (yeah, right. ;)
        
      } else {
        // Succesfully connected as Client.
        
        memset(&con->socket_status, 0x00, sizeof(con->socket_status)); // Zero socket status
        
        // Fill in POLL structure for watchin the socket status.
        con->socket_status.fd = con->socket_fd; // Set status field to current socket
        con->socket_status.events = POLLHUP | POLLERR | POLLNVAL; // set flags
        con->connectionStatus = tcpStatusConnected; // Assign current "connectedstate" flag
        
        con->data_fd = con->socket_fd; // Copy to data handle
        
        // Set TCP_NODELAY socket option.
        int flag = 1;
        int sr = setsockopt(con->data_fd,
                            IPPROTO_TCP,
                            TCP_NODELAY,
                            (char *) &flag,
                            sizeof(int));
        if (sr < 0) {
          dPrint(ODERROR,"Failed to set TCP_NODELAY Socket option!");
        }

        // Modify NON-BLOCKING state according to requested value.
        if (con->blockingType == tcpBlockingTypeNonBlocking) {
          int r = fcntl(con->data_fd, F_SETFL, O_NONBLOCK);
          assert(r == 0);
        }
        
        dPrint(ODVINFO, "Connection to target '%s' at port %d successfully completed.", 
               inet_ntoa(con->inet_addr),
               con->port);
        
        // Finally, set result to OK
        result = CONNECTIONTCP_OK;
        
      } // if (connect...)
    } // if(con->hostEntry...)
    
    // UNLOCK
    //  pthread_mutex_unlock(&con->mutex);
  }  
  return result;
}
//**************************************************************************************
  
int connectionTCPConnect(TConnectionTCP *con)
{
  int result = CONNECTIONTCP_ERROR;

  if (con->connectionStyle == tcpStyleClient) {
    result = clientConnect(con);

  } else if (con->connectionStyle == tcpStyleServer) {
    result = serverConnect(con);

  } else if (con->connectionStyle == tcpStyleListeningSocket) {
    result = listeningSocketConnect(con);

  } else if (con->connectionStyle == tcpStyleAsyncClient) {
    result = asyncClientConnect(con);
    
  }

  return result;
}
//**************************************************************************************

int connectionTCPDisconnect(TConnectionTCP *con)
{
  int result = CONNECTIONTCP_ERROR;
  
  // LOCK
  //  pthread_mutex_lock(&con->mutex);

  if (con->connectionStatus != tcpStatusDisconnected) {
  
    // Just close the socket, effectively closes the connection
    if (con->data_fd > 0) {
      shutdown(con->data_fd, SHUT_RDWR);
      close(con->data_fd);
    }
    
    // Reassign status
    con->connectionStatus = tcpStatusDisconnected;

    // Set data_fd to -1 (no longer available)
    con->data_fd = -1;

  } // if (con->connectionStatus...)
  
  // UNLOCK
  //  pthread_mutex_unlock(&con->mutex);
  
  return result;
}
//**************************************************************************************

int connectionTCPSetHost(TConnectionTCP *con, const char *hostname)
{
  int result = CONNECTIONTCP_ERROR;

  struct hostent *hostEntry = NULL;
  assert(hostname != NULL);
  
  // LOCK
  //  pthread_mutex_lock(&con->mutex);
  
  if (con->host != NULL) 
    free(con->host);
  const int len = strlen(hostname);
  assert(len < 1024);
  con->host = (char *)malloc(len+1);
  strcpy(con->host, hostname);
  
  // Do hostname lookup. Also ip addresses are valid.
  if ((hostEntry = gethostbyname(con->host)) == NULL) {
    dPrint(1, "Invalid hostname: '%s'! (%s)", hostname, hstrerror(h_errno));
    
  } else {
    // Assign address if it was valid
    con->inet_addr = *((struct in_addr *)hostEntry->h_addr);
    result = CONNECTIONTCP_OK;
  }

  // UNLOCK
  //  pthread_mutex_unlock(&con->mutex);
  
  // Return
  return result;
}
//**************************************************************************************

int connectionTCPSetIP(TConnectionTCP *con, const char *ipaddr)
{
  int result = CONNECTIONTCP_ERROR;

  // LOCK
  //  pthread_mutex_lock(&con->mutex);
  
  // call inet_aton to convert the supplied "IPv4 numbers-and-dots"
  // notation IP to source IP.
  //dPrint(ODTEST,"Setting: %s", ipaddr);
  if (inet_aton(ipaddr, &con->inet_addr) != 0) {
    result = CONNECTIONTCP_OK;
  }
  
  // UNLOCK
  //  pthread_mutex_unlock(&con->mutex);
  
  // Return
  return result;
}
//**************************************************************************************

int connectionTCPSetPort(TConnectionTCP *con, int port)
{
  if (port <= 0) {
    return CONNECTIONTCP_ERROR;
  }
  
  con->port = port;

  return CONNECTIONTCP_OK;
}
//**************************************************************************************

int connectionTCPSetBindAddr(TConnectionTCP *con, const char *address)
{
  // FIXME: No implementation for connectionTCPSetBindAddr yet!
  abort();
  return CONNECTIONTCP_OK;
}
//**************************************************************************************

int connectionTCPSendDataFormatted(TConnectionTCP *con, const char *format, ...)
{
  char buffer[2048];
  int bytes;
  va_list va; //parameter list
   
  va_start(va, format); //get list
  bytes = vsnprintf(buffer, sizeof(buffer), format, va); //print list
  va_end(va); //end list

  assert(bytes < sizeof(buffer));


  dPrint(10, "Sent:  \"%s\"", buffer);

  // Send the string (This is a wrapper, no own mutexes needed)
  bytes = connectionTCPSendData(con, (unsigned char *)buffer, bytes);

  return bytes;
}
//**************************************************************************************

int connectionTCPSendData(TConnectionTCP *con, const unsigned char *buffer, int bufLen)
{
  int num = 0,r;

  assert(buffer != NULL);
  assert(bufLen >= 0);
  
  // If buflen is Zero, return zero bytes written. (which is correct)
  if (bufLen == 0) return 0;

  // LOCK
  //  pthread_mutex_lock(&con->mutex);

  if (con->blockingType == tcpBlockingTypeBlocking) {
    // we're free to write! This guarantees the whole buffer is sent!
    do {
      r = write(con->data_fd, buffer+num, bufLen-num);
      num += r;
      if (num < bufLen) dPrint(8, "Incomplete write! %d/%d bytes written", num, bufLen);
    } while(num < bufLen && r >= 0);
    
    if(r < 0) {
      // Despite the error checking, something went wrong.
      dPrint(1, "Connection error, data sending failed!");
      con->connectionStatus = tcpStatusError;
      num = -1;
    }  
  } else {
    // Performing non-blocking write; therefore, writing as much
    // as possible, and returning the amount sent
    num = write(con->data_fd, buffer, bufLen);
    if (num < 0) {
      if (errno == EAGAIN) {
        // Connection is non-blocking, and BLOCKING state encountered,
        // so, returning Zero bytes written (which is the truth)
        num = 0;
      
      } else {
        // Otherwise, there was some error!
        num = -1;
	
      }
    }
    dPrint(8, "%d/%d bytes written", num, bufLen);
  }

  // UNLOCK
  //  pthread_mutex_unlock(&con->mutex);

  return num;
}
//**************************************************************************************

int connectionTCPReceiveDataTimed(TConnectionTCP *con, 
                                  unsigned char *buffer, 
                                  int bufLen,
                                  int timeout_ms)
{
  // Parameters
  int v;

  // Check
  assert(bufLen >= 1);
  
  if (con->data_fd < 0) {
    //dPrint(ODTEST,"Connection is not open!");
    return -1;
  }


  // Create mask which blocks all but SIGINT.
  sigset_t mask;
  sigfillset(&mask);
  sigdelset(&mask, SIGUSR1);

  // Assign poll() parameters
  struct pollfd p;
  p.fd = con->data_fd; // File descriptor
  p.events = POLLIN; // Only interested in poll_in
  p.revents = 0; // To satisfy valgrind
  
  const struct timespec tout = { timeout_ms/1000, (timeout_ms%1000)*1000000 };
  v = ppoll(&p, 1, &tout, &mask);

  // Call poll(), until it succeeds ;)
  //while ( (v = poll(&p, 1, timeout_ms)) < 0 && errno == EINTR);
  //  v = poll(&p, 1, timeout_ms);
  
  if (v == 0) {
    // dPrint(10,"Poll returned Zero - Timeout! No data to read.");
    
  } else if (v == 1 && (p.revents & POLLIN)) {
    // LOCK
    //pthread_mutex_lock(&con->mutex);
    
    v = read(con->data_fd, buffer, bufLen);
    if (v <= 0) { 
      connectionTCPDisconnect(con);
      con->connectionStatus = tcpStatusError;
      con->data_fd = -1;
      dPrint(5, "Error reading data from Socket!");   
      v = -1;
    }  
    
    // UNLOCK
    //pthread_mutex_unlock(&con->mutex);
    
  } else {
    dPrintLE(5,"Poll returned error!");
    v = -1;
  }
  
  return v;
}
//**************************************************************************************

int connectionTCPReceiveData(TConnectionTCP *con, unsigned char *buffer, int bufLen)
{
  int v;
  assert(con->data_fd != -1 && bufLen >= 1);

  // FIXME: HANDLE NON-BLOCKING/BLOCKING
  
  //  pthread_mutex_lock(&con->mutex);
  v = read(con->data_fd, buffer, bufLen);
  // pthread_mutex_unlock(&con->mutex);

  if (v < 0) { 
    connectionTCPDisconnect(con);
    con->connectionStatus = tcpStatusError;
    dPrint(1, "Error reading data from Socket!");   
    v = -1;
  }
  return v;
}
//**************************************************************************************

int connectionTCPReceiveDataBlock(TConnectionTCP *con, unsigned char *buffer, int readBytes, 
                                  int timeout)
{
  int gotBytes = 0, retVal;
  fd_set readfs;    // FIle descriptor set
  struct timeval maxWait; // Time to wait (still)

  if (con->data_fd < 0 || timeout < 0) return -1;

  FD_ZERO(&readfs);    // ZERO descriptor set.
  FD_SET(con->data_fd, &readfs); // Assign fs

  // Assign timeout values. (Convert to timeval struct)
  maxWait.tv_sec = (timeout-(timeout % 1000)) / 1000; // Full Seconds
  maxWait.tv_usec = (timeout % 1000)*1000; // Microseconds

  do {
    if (timeout > 0) {
      retVal = select(con->data_fd+1, &readfs, NULL, NULL, &maxWait); // WAIT.
      if (retVal == 0) { // Timeout
        return gotBytes; // Return incomplete packet
      } else if (retVal == -1) { // error
        return -1;
      }
    }
    // No mutex needed.
    retVal = connectionTCPReceiveData(con, buffer+gotBytes, readBytes-gotBytes);
    if (retVal < 0) return -1; // ERROR, read aborted
    if (retVal == 0) return gotBytes;
    gotBytes += retVal;
  } while(gotBytes < readBytes);
  return gotBytes; 
}
//**************************************************************************************

int connectionTCPReceiveDataTextLine(TConnectionTCP *con, char *buffer, int bufLen, char endChar)
{

  int gotBytes = 0, retVal;
  unsigned char readChar;

  if (buffer == NULL || bufLen < 1) return -1;

  do {
    retVal = connectionTCPReceiveDataBlock(con, &readChar, 1, 0); // Read one byte and sit forever for it
    if (retVal <= 0) return -1; // Connection broken BEFORE line full. Need to quit.
    *(buffer+gotBytes) = (isprint(readChar) || isspace(readChar)) ? readChar : '*';
    gotBytes++;

  } while(gotBytes < bufLen && readChar != endChar);
  return gotBytes;
}
//**************************************************************************************
//**************************************************************************************
/// gcc -Wall connectionTCP.c -DCONNECTION_DEBUG -DLINUX_OS ../utils/owndebug.c ../utils/ownutils.c -lrt -lpthread
#ifdef CONNECTION_DEBUG
#include <stdio.h>
#include <assert.h>

int main(void)
{
  int i;
  char buffer[1024];
  
  char host[100];
  int port;
  TConnectionTCP c;

  do {
    printf("Enter host and port to connect to (<host> <port>):\n");
  } while(scanf("%s %d", host, &port) != 2);

  
  printf("Connecting to \"%s\", port %d\n", host, port);

  
  assert(connectionTCPInit(&c) == 0);
  assert(connectionTCPSetHost(&c, host) == 0);
  assert(connectionTCPSetPort(&c, port) == 0);

  assert(connectionTCPConnect(&c) == 0);

  printf("\n\nEnter data to send ('q' on a empty line to quit):\n");
  do {
    for(i=0; i < sizeof(buffer); i++) {
      fflush(stdin);
      buffer[i] = getchar();
      if (buffer[0] == 'q') break;
      if (buffer[i] == '\n') {
        assert(connectionTCPSendData(&c, (unsigned char*)buffer, i+1) == i+1);
        break;
      }
    }
  } while(buffer[0] != 'q');

  printf("Receiving; Send 'q' to quit\n");
  buffer[0] = 'f';
  do {
    int bytes;
    bytes = connectionTCPReceiveData(&c, (unsigned char*)buffer, sizeof(buffer));
    assert(bytes > 0);
    buffer[bytes] = '\0';
    printf("Got: \"%s\"\n", buffer);
  } while(buffer[0] != 'q');
  


  connectionTCPDeinit(&c);
  return 0;
}
#endif //CONNECTION_DEBUG

//gcc -Wall connectionTCP.c -DSERVERCONNECTION_DEBUG -DLINUX_OS ../utils/owndebug.c ../utils/ownutils.c -lrt -lpthread
#ifdef SERVERCONNECTION_DEBUG
#include <stdio.h>
#include <assert.h>

int main(void)
{
  int i;
  char buffer[1024];
  int port;
  TConnectionTCP c[5];
  struct sockaddr_in sockaddr;

  printf("Enter port to listen: ");
  scanf("%d", &port);

  printf("Listening on port: %d\n", port);

  int socket = connectionTCPCreateListeningSocket(port, &sockaddr);

  for(i=0; i < 5; i++) {
    assert(connectionTCPInit(&c[i]) == 0);
    assert(connectionTCPSetPort(&c[i], port) == 0);
    assert(connectionTCPSetConnectionType(&c[i],  tcpStyleServer, tcpBlockingTypeBlocking) == 0);
  }

  for(i=0; i < 5; i++) { // Connect rest
    assert(connectionTCPSetSocketHandle(&c[i], socket, &sockaddr) == 0);
    assert(connectionTCPConnect(&c[i]) == 0);
  }

  do {
    int bytes;
    bytes = connectionTCPReceiveData(&c[0], (unsigned char *)buffer, sizeof(buffer));
    if (bytes <= 0) break;
    buffer[bytes] = '\0';
    printf("Got: \"%s\"\n", buffer);
  } while(buffer[0] != 'q');
  

  for(i=0; i < 5; i++) {
    connectionTCPDeinit(&c[i]);
  }
  return 0;
}
#endif //CONNECTION_DEBUG
//**************************************************************************************
//**************************************************************************************
//**************************************************************************************
//**************************************************************************************
//**************************************************************************************

